/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package com.velocityme.session;

import com.velocityme.entity.Login;
import com.velocityme.entity.Node;
import com.velocityme.entity.Person;
import com.velocityme.entity.User;
import com.velocityme.enums.LoginStatus;
import com.velocityme.enums.Permission;
import com.velocityme.utility.InvalidKeyException;
import com.velocityme.utility.PasswordEncrypter;
import com.velocityme.utility.PermissionDeniedException;
import java.util.Date;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.ejb.EJB;
import javax.ejb.Stateful;
import javax.naming.ServiceUnavailableException;
import javax.persistence.EntityManager;
import javax.persistence.NoResultException;
import javax.persistence.PersistenceContext;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 *
 * @author rcrida
 */
@Stateful
public class KeyBean implements KeyLocal {
    @EJB
    private SetupLocal setupBean;

    @EJB
    private SearchLocal searchBean;

    @PersistenceContext
    private EntityManager em;

    private Login m_login;

    @EJB
    private LicenceKeyLocal licenceKeyLocal;

    @EJB
    private LoginLocal loginLocal;

    private final Log m_logger = LogFactory.getLog(KeyBean.class);

    /**
     * Attempts to login a user to the system by looking up the user object,
     * checking the password provided is valid and then setting m_user to make
     * the key valid. If any steps fail then the method returns false.
     *
     * @param username Name of user trying to login
     * @param password Password of user trying to login
     * @param remoteHost Host address of the client machine
     * @return success code
     **/
    public Success login(final String username, final String password, final String remoteHost) {
        // Check that the database has been configured
        setupBean.setupDatabase(username, password);
        // Load the login statuses once we know they will be available!
        try {
            User user = (User)em.createNamedQuery("findUserByName")
                                .setParameter("name", username)
                                .getSingleResult();
            if (user.isEnabled()) {
                try {
                    if ((user.getPasswordHash().equals("") && password.equals("")) || user.getPasswordHash().equals(PasswordEncrypter.getInstance().encrypt(password))) {
                        // now we must try to assign a suitable licence
                        m_login = licenceKeyLocal.assignLicense(user, remoteHost);
                        if (m_login != null) {
                            loginLocal.limitLoginCount(user);
                            m_logger.info("User " + username + " has logged in successfully");
                            return Success.SUCCESS;
                        } else {
                            m_logger.warn("User " + username + " could not log in due to no available licence");
                            return Success.NO_AVAILABLE_LICENCE;
                        }
                    } else {
                        m_logger.warn("User " + username + " provided incorrect password");
                        return Success.INCORRECT_PASSWORD;
                    }
                } catch (ServiceUnavailableException ex) {
                    Logger.getLogger(KeyBean.class.getName()).log(Level.SEVERE, null, ex);
                    return Success.ERROR;
                }
            }
            else {
                m_logger.warn("Disabled user " + username + " attempting to login");
                return Success.INVALID_USER;
            }
        } catch (NoResultException ex) {
            m_logger.warn("Invalid user " + username + " attempting to login");
            return Success.UNKNOWN_USER;
        }
    }

    /**
     * Logs out a user and invalidates the key
     **/
    public void logout() {
        if (isValid()) {
            m_logger.info("User " + getUser().getName() + " logged out");
            licenceKeyLocal.releaseLicence(m_login);
        }
        m_login.setLogoutTime(new Date());
        m_login.setLoginStatus(LoginStatus.LOGGED_OFF);
        em.merge(m_login);
    }

    /**
     * Checks to see if this key is currently valid
     **/
    public boolean isValid() {
        try {
            m_login.setLastActionTime(new Date());
            em.merge(m_login);
            if (!m_login.getLoginStatus().equals(LoginStatus.LOGGED_ON))
                return false;
            return getUser().isEnabled() == true;
        } catch(Exception e) {
            m_logger.error("Trying to access deleted login");
            return false;
        }

    }

    /**
     * Gets the username
     **/
    public String getUsername() {
        if (isValid()) {
            return getUser().getName();
        }
        return "Invalid key!";

    }

    /**
     * Does the user want to see bookmarks?
     **/
    public boolean getShowBookmarks() {
        if (isValid()) {
            return getUser().isShowBookmarks() == true;
        }
        return false;
    }

    /**
     * Gets the person name
     *
     * @ejb.interface-method view-type="remote"
     **/
    public String getPersonName() {
        if (isValid()) {
            return getUser().getPerson().getName();
        }
        return "Invalid key!";
    }

    /**
     * Access to the user holding the key
     **/
    public User getUser() {
        return m_login.getUser();
    }

    /**
     * Access to the login record
     **/
    public Login getLogin() {
        return null;
    }

    public void validateKey() {
        if (!isValid()) {
            throw new InvalidKeyException();
        }
    }

    public void assertHasPermission(Node node, Permission permission) {
        if (!searchBean.getAssignedPermissions(this, node).contains(permission)) {
            throw new PermissionDeniedException();
        }
    }

    public Person getPerson() {
        return m_login.getUser().getPerson();
    }


    // Add business logic below. (Right-click in editor and choose
    // "Insert Code > Add Business Method" or "Web Service > Add Operation")
    
}
